import Joi from 'joi';
// import { pathOr } from 'ramda';
import ipLimit from '../ipLimit';

export const sourceIp = Joi.string()
	.allow(null, '')
	.ip({ version: ['ipv4', 'ipv6'] })
	.message('IP地址不合法')
	.custom((value, helpers) => {
		if (value) {
			if (ipLimit(value)) {
				return helpers.message('当前域名的绑定IP是保留地址');
			}
		}
		return value;
	}, 'sourceIp validation')
	.description('目标源IP');

export const securityStatus = Joi.string()
	.meta({
		_mongoose: {
			enum: ['unknown', 'safe', 'warning', 'resolved', 'abnormalResolved'],
		},
	})
	.default('unknown')
	.description('任务监测结果状态。unknown(未知)/safe(正常)/warning(异常)');

export const interval = Joi.number()
	.min(1)
	.empty(null, '')
	.description('定时任务配置');

// const vulLevel = Joi.object({
// 	high: Joi.boolean().default(false),
// 	medium: Joi.boolean().default(false),
// 	low: Joi.boolean().default(false),
// })
// 	.allow(null)
// 	.description('漏洞告警等级');

export const taskSettings = Joi.object({
	interval,
	sourceIp,
	_id: Joi.string().allow(null, ''),
	_cls: Joi.string().strip().description('python 模型自动添加的'),
	enable: Joi.boolean().allow(null).description('是否启用'),
	enableSitePortraitTriggerMonitor: Joi.boolean()
		.default(false)
		.description('通过网站画像触发增量扫描'),
	taskId: Joi.string().allow(null, ''),
	// name: Joi.string()
	// 	.allow(null, '')
	// 	.empty(null, '')
	// 	.custom((value, helpers) => {
	// 		if (value === '') {
	// 			return helpers.message('任务名称不能为空');
	// 		}
	// 		if (!value) return value;

	// 		if (value?.length > 20) {
	// 			return helpers.message('任务名称不可超过20个字符');
	// 		}
	// 		return value;
	// 	}, 'name validation')
	// 	.description('任务名'),
	name: Joi.any().description('任务名'),
	// target: Joi.string()
	// 	.allow(null, '')
	// 	.empty(null, '')
	// 	.custom((value, helpers) => {
	// 		if (!value) return value;

	// 		let field = pathOr([], ['state', 'path'], helpers);
	// 		field = pathOr('', [field.length - 3], field);
	// 		const { host, protocol } = pathOr(
	// 			{},
	// 			['state', 'ancestors', 2, 'target'],
	// 			helpers
	// 		);

	// 		if (!host) return value;

	// 		try {
	// 			if (field === 'pingSettings') {
	// 				return new URL(`${protocol}//${host}`).toString();
	// 			} else if (field === 'httpSettings') {
	// 				const target = new URL(value);
	// 				target.host = host;
	// 				target.protocol = protocol;
	// 				return target.toString();
	// 			}
	// 		} catch (error) {
	// 			return helpers.message('无效的域名');
	// 		}

	// 		return value;
	// 	}, 'host validation')
	// 	.messages({ 'string.empty': '目标地址不能为空' })
	// 	.description('目标地址'),
	target: Joi.any().description('目标地址'),

	collect: Joi.object()
		.keys({
			// vul
			type: Joi.string()
				.valid('full', 'increment')
				.empty(null, '')
				.description('采集规则'),
			speed: Joi.string().empty(null).description('监测速度'),
			depth: Joi.number().empty(null).description('监测深度'),
			maxPage: Joi.number().empty(null).description('最大页面数'),
			area: Joi.array()
				.items(Joi.string())
				.empty(null)
				.description('可用性任务监测节点'),
			includeUrl: Joi.array()
				.items(Joi.string())
				.empty(null)
				.description('漏洞扫描指定URL'),
			plugins: Joi.array()
				.items(Joi.string())
				.min(1)
				.empty(null)
				.messages({
					'any.required': '监测项目不能为空',
					'array.min': '监测项目不能为空',
				})
				.description('安全事件扫描插件'),
			pluginCategories: Joi.array()
				.empty(null)
				.items(Joi.string())
				.min(1)
				.messages({ 'array.min': '监测项目不能为空' })
				.description('SSL扫描插件'),
		})
		.unknown()
		.default({})
		.allow(null)
		.description('扫描设置')
		.label('TaskSettingsCollect'),
	alert: Joi.object()
		.keys({
			// enable: Joi.boolean().default(true),

			// vul
			// vulLevel,
			vulType: Joi.array()
				.empty(null)
				.items(Joi.string())
				.min(1)
				.messages({ 'array.min': '监测项目不能为空' })
				.description('漏洞告警类型'),

			// aviliable
			respTime: Joi.number()
				.default(12000)
				.min(1)
				.message('响应时间最大值不能为空')
				.description('响应时间'),
			lossRate: Joi.number().default(50).min(1).max(100).description('丢包率'),
			nodeCount: Joi.number()
				// .default(2)
				.min(1)
				.message('最大告警节点数不能为空')
				.description('监测节点'),
			alertCount: Joi.number()
				.default(3)
				.min(1)
				.message('最大告警数不能为空')
				.description('告警数'),
		})
		.unknown()
		.default({})
		.allow(null)
		.description('告警条件设置')
		.label('TaskSettingsAlertRule'),
})
	.allow(null)
	.label('TaskSettings')
	.description('监测任务设置');

export const triggerType = Joi.string()
	.meta({
		_mongoose: {
			enum: [
				'schedule', // 周期 | 周期全量
				'manual', // 自定义
				'scheduleIncrement', // 周期增量
				'special', // 专项扫描
			],
		},
	})
	.default('schedule')
	.description('触发方式');

export const alertSettings = Joi.object({
	alertGroupIds: Joi.array().items(Joi.string()).description('通知组'),
	enable: Joi.object({
		asset: Joi.boolean().default(false).description('资产变动扫描'),
		vul: Joi.boolean().default(false).description('漏洞监测'),
		cyd: Joi.boolean().default(false).description('创宇盾配置'),
		ssl: Joi.boolean().default(false).description('SSL'),
		securityEvent: Joi.boolean().default(false).description('内容安全'),
		http: Joi.boolean().default(false).description('HTTP'),
		ping: Joi.boolean().default(false).description('Ping'),
		content: Joi.boolean().default(false).description('content'),
		hostVul: Joi.boolean().default(false).description('主机资产'),
	})
		.empty(true, false, null)
		.default({
			asset: false,
			vul: false,
			cyd: false,
			ssl: false,
			securityEvent: false,
			http: false,
			ping: false,
		})
		.description('启用告警通知'),
	notification: Joi.object({
		email: Joi.boolean().default(false).description('邮件'),
		sms: Joi.boolean().default(false).description('短信'),
		wechat: Joi.boolean().default(false).description('微信'),
		inBox: Joi.boolean().default(false).description('站内信'),
	}),
	smsAllowedTime: Joi.object({
		start: Joi.string()
			.pattern(/^\d{2}:\d{2}:\d{2}$/)
			.allow(null),
		end: Joi.string()
			.pattern(/^\d{2}:\d{2}:\d{2}$/)
			.allow(null),
	}).description('短信通知时段'),
	emailAllowedTime: Joi.object({
		start: Joi.string()
			.pattern(/^\d{2}:\d{2}:\d{2}$/)
			.allow(null),
		end: Joi.string()
			.pattern(/^\d{2}:\d{2}:\d{2}$/)
			.allow(null),
	}).description('邮件通知时段'),
	smsDailyLimit: Joi.number().default(0).description('短信通知每日限制'),
	emailDailyLimit: Joi.number()
		.default(0)
		.description('邮件通知短信通知每日限制时段'),
}).description('告警通知发送设置');

export const warnings = Joi.array()
	.items(
		Joi.object({
			// 用于从结果中找到详情
			id: Joi.string(),
			title: Joi.string(),
			type: Joi.string().valid('add', 'remove').empty(null),
			category: Joi.string().empty(null).allow('').default(''),
			affects: Joi.string().allow(null, '').description('影响目标'),
			description: Joi.string()
				.empty(null)
				.allow('')
				.default('')
				.description('描述'),
			impact: Joi.string()
				.empty(null)
				.allow('')
				.default('')
				.description('影响'),
			level: Joi.number()
				.meta({
					_mongoose: {
						enum: [1, 2, 3],
					},
				})
				.empty(null)
				.default(1)
				.required()
				.description('威胁等级'),
			levelName: Joi.string().description('威胁等级名称'),
			detail: Joi.any().allow(null),
			addition: Joi.object().allow(null),
			isResolved: Joi.boolean()
				.default(false)
				.description('告警是否标记为已修复'),
			ignoredTime: Joi.date().allow(null).description('告警忽略时间'),
			detailText: Joi.string().description('告警详细描述'),
			recommendation: Joi.string().description('建议'),
			reference: Joi.array().items(Joi.string()).description('参考文献'),
			cnvd: Joi.array().items(Joi.string()).description('漏洞id'),
			foundAt: Joi.date().allow(null).description('发现时间'),
			traffics: Joi.any().description('请求体等数据'),
			payload: Joi.any().description('脆弱性证据'),
		})
	)
	.allow(null);

export const alertState = Joi.object({
	securityStatus,
	firstSendTime: Joi.date().allow(null),
	lastSendTime: Joi.date().allow(null),
	sendCount: Joi.number().default(0),
}).description('告警发送状态/计数');
export const diff = (obj1, obj2) => {
	const result = {};
	if (Object.is(obj1, obj2)) {
		return undefined;
	}
	if (!obj2 || typeof obj2 !== 'object') {
		return obj2;
	}
	Object.keys(obj1 || {})
		.concat(Object.keys(obj2 || {}))
		.forEach(key => {
			if (obj2[key] !== obj1[key] && !Object.is(obj1[key], obj2[key])) {
				result[key] = obj2[key];
			}
			if (typeof obj2[key] === 'object' && typeof obj1[key] === 'object') {
				const value = diff(obj1[key], obj2[key]);
				if (value !== undefined) {
					result[key] = value;
				}
			}
		});
	return result;
};
export const filterNull = obj => {
	const o = deepClone(obj);
	for (var key in o) {
		if (o[key] === null || o[key] === '' || o[key] === undefined) {
			delete o[key];
		}
		if (toType(o[key]) === 'string') {
			o[key] = o[key].trim();
		} else if (toType(o[key]) === 'object') {
			o[key] = filterNull(o[key]);
		} else if (toType(o[key]) === 'array') {
			o[key] = filterNull(o[key]);
		}
	}
	return o;
};
export const deepClone = data => {
	var type = getObjType(data);
	var obj;
	if (type === 'array') {
		obj = [];
	} else if (type === 'object') {
		obj = {};
	} else {
		// 不再具有下一层次
		return data;
	}
	if (type === 'array') {
		for (var i = 0, len = data.length; i < len; i++) {
			obj.push(deepClone(data[i]));
		}
	} else if (type === 'object') {
		for (var key in data) {
			obj[key] = deepClone(data[key]);
		}
	}
	return obj;
};
// 过滤对象空值
export const toType = obj => {
	return {}.toString
		.call(obj)
		.match(/\s([a-zA-Z]+)/)[1]
		.toLowerCase();
};
export const getObjType = obj => {
	var toString = Object.prototype.toString;
	var map = {
		'[object Boolean]': 'boolean',
		'[object Number]': 'number',
		'[object String]': 'string',
		'[object Function]': 'function',
		'[object Array]': 'array',
		'[object Date]': 'date',
		'[object RegExp]': 'regExp',
		'[object Undefined]': 'undefined',
		'[object Null]': 'null',
		'[object Object]': 'object',
	};
	if (obj instanceof Element) {
		return 'element';
	}
	return map[toString.call(obj)];
};
